Skip to content

feat(mobile): add initialPage prop to Carousel - #864

Merged
hcopp merged 2 commits into
coinbase:masterfrom
caitlin-coyiuto-cb:ccoyiuto/CB1-2048
Aug 28, 2026
Merged

feat(mobile): add initialPage prop to Carousel#864
hcopp merged 2 commits into
coinbase:masterfrom
caitlin-coyiuto-cb:ccoyiuto/CB1-2048

Conversation

@caitlin-coyiuto-cb

@caitlin-coyiuto-cb caitlin-coyiuto-cb commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

CB1-2048

What changed? Why?

  • Add an uncontrolled initialPage prop to the cds-mobile Carousel so consumers can open it on a non-zero page with no animation.
  • Seed activePageIndex at init + a one-time, didInitRef-guarded non-animated seek on first measurement (instant .set(), no slide from page 0); reads initialPage via a ref since data can arrive async.
  • No onChangePage on mount; clamps out-of-range; routes through findNearestLoopOffset when looping; a later goToPage/drag still wins.
  • Replaces the consumer-side requestAnimationFrame + goToPage workaround (clears TODO(CB1-2026)); unblocks the RN adoption PR (PR 2).

Root cause (required for bugfixes)

N/A — feature, not a bugfix.

UI changes

Adds an "Initial Page (opens on page 3)" example to the mobile Carousel stories. Web unaffected.

CB1-2048-cds.mov
CB1-2048-cds-web.mov

Testing

How has it been tested?

  • Unit tests
  • Interaction tests
  • Pseudo State tests
  • Manual - Web
  • Manual - Android (Emulator / Device)
  • Manual - iOS (Emulator / Device)

Testing instructions

Automated: New Initial Page tests in Carousel.test.tsx — opens on initialPage; onChangePage not called on mount; out-of-range clamps (99→6, -5→0); later goToPage and drag override the initial page. Full file 97/97 pass; mobile typecheck + lint clean.

Manual: yarn nx run expo-app:ios → open the Carousel route → the "Initial Page (opens on page 3)" example lands on page 3 with no slide from page 1; dragging/pagination still work.

Illustrations/Icons Checklist

N/A — no changes under packages/illustrations/** or packages/icons/**.

Change management

type=routine
risk=low
impact=sev5

automerge=false

@cb-heimdall

cb-heimdall commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

✅ Heimdall Review Status

Requirement Status More Info
Reviews 1/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 1
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1
CODEOWNERS ✅ See below

CODEOWNERS

Code Owner Status Calculation
ui-systems-eng-team 1/1
Denominator calculation
Additional CODEOWNERS Requirement
Show calculation
Sum 0
0
From CODEOWNERS 1
Sum 1

@caitlin-coyiuto-cb caitlin-coyiuto-cb changed the title feat(mobile): add initialPage prop to Carousel feat(carousel): add initialPage prop Aug 28, 2026
@caitlin-coyiuto-cb caitlin-coyiuto-cb changed the title feat(carousel): add initialPage prop feat(mobile): add initialPage prop to Carousel Aug 28, 2026
@caitlin-coyiuto-cb
caitlin-coyiuto-cb marked this pull request as ready for review August 28, 2026 16:22

@hcopp hcopp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Had some feedback

Comment thread packages/web/CHANGELOG.md
Comment thread packages/mobile/src/carousel/Carousel.tsx
Comment thread packages/mobile/src/carousel/Carousel.tsx Outdated
Comment thread packages/mobile/src/carousel/Carousel.tsx Outdated
Comment thread packages/mobile/src/carousel/Carousel.tsx Outdated
});

const [activePageIndex, setActivePageIndex] = useState(0);
const [activePageIndex, setActivePageIndex] = useState(() => Math.max(0, initialPage ?? 0));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably use the value from the ref here in case the initialPage prop does in fact change. If that were the case then the activePage state would also change

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@caitlin-coyiuto-cb I think we need to clamp this with the max as well right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can here on first render since it'd be 0? totalPages gets derived on line 725 later. Also have tests for clamping on last page (if initialPage > last page) and negative initialPage

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was wondering if we needed to set activePageIndex when we set hasAppliedInitialPageRef to true. I suppose goToPage does clamp automatically but I wasn't sure if there is an interim side effects if this value was way off.

Comment thread packages/mobile/src/carousel/Carousel.tsx Outdated
@caitlin-coyiuto-cb
caitlin-coyiuto-cb force-pushed the ccoyiuto/CB1-2048 branch 3 times, most recently from 684136f to b8a7736 Compare August 28, 2026 19:39
Comment thread packages/web/src/carousel/__stories__/Carousel.stories.tsx Outdated
Comment thread packages/mobile/src/carousel/Carousel.tsx
Comment thread packages/mobile/src/carousel/Carousel.tsx Outdated
caitlin-coyiuto-cb and others added 2 commits August 28, 2026 13:41
Add an uncontrolled `initialPage` prop to the cds-mobile and cds-web Carousel so
a consumer can open it on a non-zero page with no animation. Seeds
`activePageIndex` at init (no `onChangePage` on mount) and performs a one-time,
`didInitRef`-guarded non-animated seek on the first successful measurement —
reading `initialPage` via a ref and routing through `findNearestLoopOffset` when
looping. Adds `initialPage` tests and a story example on both platforms.

CB1-2048

Co-Authored-By: Claude <noreply@anthropic.com>

@hcopp hcopp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for all the updates

@hcopp
hcopp merged commit 137fe96 into coinbase:master Aug 28, 2026
28 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants